// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/backoff_entry.h"

#include "base/logging.h"
#include "base/macros.h"
#include "base/time/tick_clock.h"
#include "base/values.h"
#include "net/base/backoff_entry_serializer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

    using base::Time;
    using base::TimeDelta;
    using base::TimeTicks;

    BackoffEntry::Policy base_policy = {
        0 /* num_errors_to_ignore */,
        1000 /* initial_delay_ms */,
        2.0 /* multiply_factor */,
        0.0 /* jitter_factor */,
        20000 /* maximum_backoff_ms */,
        2000 /* entry_lifetime_ms */,
        false /* always_use_initial_delay */
    };

    class TestTickClock : public base::TickClock {
    public:
        TestTickClock() { }
        ~TestTickClock() override { }

        TimeTicks NowTicks() override { return now_ticks_; }
        void set_now(TimeTicks now) { now_ticks_ = now; }

    private:
        TimeTicks now_ticks_;

        DISALLOW_COPY_AND_ASSIGN(TestTickClock);
    };

    TEST(BackoffEntrySerializerTest, SerializeNoFailures)
    {
        Time original_time = Time::Now();
        TestTickClock original_ticks;
        original_ticks.set_now(TimeTicks::Now());
        BackoffEntry original(&base_policy, &original_ticks);
        std::unique_ptr<base::Value> serialized = BackoffEntrySerializer::SerializeToValue(original, original_time);

        std::unique_ptr<BackoffEntry> deserialized = BackoffEntrySerializer::DeserializeFromValue(
            *serialized, &base_policy, &original_ticks, original_time);
        ASSERT_TRUE(deserialized.get());
        EXPECT_EQ(original.failure_count(), deserialized->failure_count());
        EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime());
    }

    TEST(BackoffEntrySerializerTest, SerializeTimeOffsets)
    {
        Time original_time = Time::FromJsTime(1430907555111); // May 2015 for realism
        TestTickClock original_ticks;
        BackoffEntry original(&base_policy, &original_ticks);
        // 2 errors.
        original.InformOfRequest(false);
        original.InformOfRequest(false);
        std::unique_ptr<base::Value> serialized = BackoffEntrySerializer::SerializeToValue(original, original_time);

        {
            // Test that immediate deserialization round-trips.
            std::unique_ptr<BackoffEntry> deserialized = BackoffEntrySerializer::DeserializeFromValue(
                *serialized, &base_policy, &original_ticks, original_time);
            ASSERT_TRUE(deserialized.get());
            EXPECT_EQ(original.failure_count(), deserialized->failure_count());
            EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime());
        }

        {
            // Test deserialization when wall clock has advanced but TimeTicks::Now()
            // hasn't (e.g. device was rebooted).
            Time later_time = original_time + TimeDelta::FromDays(1);
            std::unique_ptr<BackoffEntry> deserialized = BackoffEntrySerializer::DeserializeFromValue(
                *serialized, &base_policy, &original_ticks, later_time);
            ASSERT_TRUE(deserialized.get());
            EXPECT_EQ(original.failure_count(), deserialized->failure_count());
            // Remaining backoff duration continues decreasing while device is off.
            // Since TimeTicks::Now() has not advanced, the absolute release time ticks
            // will decrease accordingly.
            EXPECT_GT(original.GetTimeUntilRelease(),
                deserialized->GetTimeUntilRelease());
            EXPECT_EQ(original.GetReleaseTime() - TimeDelta::FromDays(1),
                deserialized->GetReleaseTime());
        }

        {
            // Test deserialization when TimeTicks::Now() has advanced but wall clock
            // hasn't (e.g. it's an hour later, but a DST change cancelled that out).
            TestTickClock later_ticks;
            later_ticks.set_now(TimeTicks() + TimeDelta::FromDays(1));
            std::unique_ptr<BackoffEntry> deserialized = BackoffEntrySerializer::DeserializeFromValue(
                *serialized, &base_policy, &later_ticks, original_time);
            ASSERT_TRUE(deserialized.get());
            EXPECT_EQ(original.failure_count(), deserialized->failure_count());
            // According to the wall clock, no time has passed. So remaining backoff
            // duration is preserved, hence the absolute release time ticks increases.
            // This isn't ideal - by also serializing the current time and time ticks,
            // it would be possible to detect that time has passed but the wall clock
            // went backwards, and reduce the remaining backoff duration accordingly,
            // however the current implementation does not do this as the benefit would
            // be somewhat marginal.
            EXPECT_EQ(original.GetTimeUntilRelease(),
                deserialized->GetTimeUntilRelease());
            EXPECT_EQ(original.GetReleaseTime() + TimeDelta::FromDays(1),
                deserialized->GetReleaseTime());
        }

        {
            // Test deserialization when both wall clock and TimeTicks::Now() have
            // advanced (e.g. it's just later than it used to be).
            TestTickClock later_ticks;
            later_ticks.set_now(TimeTicks() + TimeDelta::FromDays(1));
            Time later_time = original_time + TimeDelta::FromDays(1);
            std::unique_ptr<BackoffEntry> deserialized = BackoffEntrySerializer::DeserializeFromValue(*serialized, &base_policy,
                &later_ticks, later_time);
            ASSERT_TRUE(deserialized.get());
            EXPECT_EQ(original.failure_count(), deserialized->failure_count());
            // Since both have advanced by the same amount, the absolute release time
            // ticks should be preserved; the remaining backoff duration will have
            // decreased of course, since time has passed.
            EXPECT_GT(original.GetTimeUntilRelease(),
                deserialized->GetTimeUntilRelease());
            EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime());
        }

        {
            // Test deserialization when wall clock has gone backwards but TimeTicks
            // haven't (e.g. the system clock was fast but they fixed it).
            EXPECT_LT(TimeDelta::FromSeconds(1), original.GetTimeUntilRelease());
            Time earlier_time = original_time - TimeDelta::FromSeconds(1);
            std::unique_ptr<BackoffEntry> deserialized = BackoffEntrySerializer::DeserializeFromValue(
                *serialized, &base_policy, &original_ticks, earlier_time);
            ASSERT_TRUE(deserialized.get());
            EXPECT_EQ(original.failure_count(), deserialized->failure_count());
            // If only the absolute wall clock time was serialized, subtracting the
            // (decreased) current wall clock time from the serialized wall clock time
            // could give very large (incorrect) values for remaining backoff duration.
            // But instead the implementation also serializes the remaining backoff
            // duration, and doesn't allow the duration to increase beyond it's previous
            // value during deserialization. Hence when the wall clock goes backwards
            // the remaining backoff duration will be preserved.
            EXPECT_EQ(original.GetTimeUntilRelease(),
                deserialized->GetTimeUntilRelease());
            // Since TimeTicks::Now() hasn't changed, the absolute release time ticks
            // will be equal too in this particular case.
            EXPECT_EQ(original.GetReleaseTime(), deserialized->GetReleaseTime());
        }
    }

} // namespace

} // namespace net
